#include "commons.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void reset_ppu() {
    emu->ppu->name_table_address = 0;
    emu->ppu->bkg_pattern_table_address = 0;
    emu->ppu->generate_nmi_on_vblank = 0;
    emu->ppu->scanline = 0;
    emu->ppu->spr_overflow = 0;
    emu->ppu->spr_0_hit = 0;
    emu->ppu->in_vblank = 1;
    emu->ppu->vram_address = 0;
    emu->ppu->vram_address_increment = 0;
    emu->ppu->mirroring = 0;
    emu->ppu->open_bus = 0;
    emu->ppu->vram_buffer = 0;
    emu->ppu->vram_latch = 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void run_scanline() {
    if( emu->ppu->scanline == 0 ) {
        trace_pattern_tables();
    }
    
    else if( emu->ppu->scanline == 242 ) {
        emu->ppu->in_vblank = 1;
        // FIXME
        //printf( "%02X %02X", emu->ppu->generate_nmi_on_vblank, read_memory(0x2002) ); getch();
        if( emu->ppu->generate_nmi_on_vblank ) handle_interrupt( INTERRUPT_NMI );
    }
    
    else if( emu->ppu->scanline == 262 ) {
        // FIXME
        //printf( "%02X", emu->ppu->in_vblank ); getch();
        emu->ppu->in_vblank = 0;
        emu->ppu->scanline = -1;
    }
    
    emu->ppu->scanline++;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BYTE read_ppu_2002() {
    BYTE data = 0;

    if( emu->ppu->spr_overflow ) data |= PPU_SPR_OVERFLOW;
    if( emu->ppu->spr_0_hit ) data |= PPU_SPR_0_HIT;
    if( emu->ppu->in_vblank ) data |= PPU_IN_VBLANK;
    emu->ppu->vram_latch = 0;
    //emu->ppu->in_vblank = 0;
    
    return data;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BYTE read_ppu_2007( WORD address ) {
    BYTE result = 0;
    
    // la direccion no corresponde a la paleta
    if( address < 0x3f00 ) {
        result = emu->ppu->vram_buffer;
        if( address < 0x2000 ) { /* TODO: mappers */ }
        else emu->ppu->vram_buffer = emu->vram[ emu->ppu->vram_address ];
    
    // paleta
    } else {
        result = emu->vram[ emu->ppu->vram_address ];
    }
    
    emu->ppu->vram_address += emu->ppu->vram_address_increment;
    
    return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void write_ppu_2000( BYTE value ) {
    // FIXME
    //printf( "WRITE @ 2002 = %02X", value ); getch();
    emu->ppu->name_table_address = ( ((value & 0x03) << 10) | 0x2000 );
    emu->ppu->vram_address_increment = ( test_bit(value, 2) ? 32 : 1 );
    emu->ppu->bkg_pattern_table_address = ( (value & 0x10) << 8 );
    emu->ppu->generate_nmi_on_vblank = test_bit( value, 7 );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void write_ppu_2001( BYTE value ) { /* TODO: PPU status */ }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void write_ppu_2003( BYTE value ) { /* TODO: sprites */ }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void write_ppu_2004( BYTE value ) { /* TODO: sprites */ }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void write_ppu_2005( BYTE value ) {
    /* TODO: scrolling */
    //emu->ppu->vram_latch = ( !emu->ppu->vram_latch );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void write_ppu_2006( BYTE value ) {
    if( emu->ppu->vram_latch ) emu->ppu->vram_address = ( value << 8 );
    else emu->ppu->vram_address |= value;
    /*if( emu->ppu->vram_latch ) emu->ppu->vram_address = value;
    else emu->ppu->vram_address |= ( value << 8 );*/
    
    emu->ppu->vram_latch = ( !emu->ppu->vram_latch );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void write_ppu_2007( BYTE value ) {
    // mapper
    if( emu->ppu->vram_address < 0x2000 ) { /* TODO: mappers */ }
    
    // name tables
    else if( (emu->ppu->vram_address >= 0x2000) && (emu->ppu->vram_address < 0x3f00) ) {
        /* TODO: mirroring */
        //emu->vram[ emu->ppu->vram_address & 0x3fff ] = value;
        if( emu->ppu->mirroring == MIRRORING_HORIZONTAL ) {
            switch( emu->ppu->vram_address & 0x2c00 ) {
                case 0x2000: emu->vram[ emu->ppu->vram_address ] = emu->vram[ emu->ppu->vram_address + 0x0400 ] = value; break;
                case 0x2400: emu->vram[ emu->ppu->vram_address ] = emu->vram[ emu->ppu->vram_address - 0x0400 ] = value; break;
                case 0x2800: emu->vram[ emu->ppu->vram_address ] = emu->vram[ emu->ppu->vram_address + 0x0400 ] = value; break;
                case 0x2c00: emu->vram[ emu->ppu->vram_address ] = emu->vram[ emu->ppu->vram_address - 0x0400 ] = value; break;
            }
        } else if( emu->ppu->mirroring == MIRRORING_VERTICAL ) {
            switch( emu->ppu->vram_address & 0x2c00 ) {
                case 0x2000: emu->vram[ emu->ppu->vram_address ] = emu->vram[ emu->ppu->vram_address + 0x0800 ] = value; break;
                case 0x2400: emu->vram[ emu->ppu->vram_address ] = emu->vram[ emu->ppu->vram_address + 0x0800 ] = value; break;
                case 0x2800: emu->vram[ emu->ppu->vram_address ] = emu->vram[ emu->ppu->vram_address - 0x0800 ] = value; break;
                case 0x2c00: emu->vram[ emu->ppu->vram_address ] = emu->vram[ emu->ppu->vram_address - 0x0800 ] = value; break;
            }
        }
    }
    
    // paleta
    else if( emu->ppu->vram_address >= 0x3f00 ) {
        /* TODO: paleta */
        // FIXME: no se si esto es correcto...
        emu->vram[ emu->ppu->vram_address ] = ( value & 0x3f );
        if( (emu->ppu->vram_address == 0x3f00) || (emu->ppu->vram_address == 0x3f10)) emu->vram[0x3f00] = emu->vram[0x3f10] = value & 0x3f;
        if( (emu->ppu->vram_address == 0x3f04) || (emu->ppu->vram_address == 0x3f14)) emu->vram[0x3f04] = emu->vram[0x3f14] = value & 0x3f;
        if( (emu->ppu->vram_address == 0x3f08) || (emu->ppu->vram_address == 0x3f18)) emu->vram[0x3f08] = emu->vram[0x3f18] = value & 0x3f;
        if( (emu->ppu->vram_address == 0x3f0c) || (emu->ppu->vram_address == 0x3f1c)) emu->vram[0x3f0c] = emu->vram[0x3f1c] = value & 0x3f;
    }
    
    emu->ppu->vram_address += emu->ppu->vram_address_increment;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////